package library;

import java.io.PrintStream;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class Library
{
	private Map<String, Set<Item>> Keywords = new TreeMap<String, Set<Item>>();
	
	private Map<String, Set<Item>> BookByAuthor = new TreeMap<String, Set<Item>>();
	private Set<Item> Books = new TreeSet<Item>(new ItemComparator());
	
	private Map<String, Set<Item>> MusicByBand = new TreeMap<String, Set<Item>>();
	private Map<String, Set<Item>> MusicByMusician = new TreeMap<String, Set<Item>>();
	private Set<Item> MusicAlbums = new TreeSet<Item>(new ItemComparator());
	
	private Map<String, Set<Item>> MoviesByDirector = new TreeMap<String, Set<Item>>();
	private Map<String, Set<Item>> MoviesByActor = new TreeMap<String, Set<Item>>();
	private Set<Item> Movies = new TreeSet<Item>(new ItemComparator());
	
	
	class ItemComparator implements Comparator<Item>{  
	    public int compare(Item c1, Item c2) {  
	        if( c1.getTitle().compareTo( c2.getTitle() ) < 0)return -1;  
	        if( c1.getTitle().compareTo( c2.getTitle() ) > 0)return 1;            
	        return 0;  
	    }
	}
	
	// general methods
	
	// returns all of the items which have the specified keyword
	public Collection<Item> itemsForKeyword(String keyword)
	{
		return Keywords.get(keyword);
	}
	
	// print an item from this library to the output stream provided
	public void printItem(PrintStream out, Item item)
	{
		out.println(item);
	}
	
	// book-related methods
	
	// adds a book to the library
	public Item addBook(String title, String author, int nPages, String... keywords)
	{
		Book B = new Book(title, author, nPages, keywords);
		
//		category by keywords
		for(String k: keywords){
			if ( !Keywords.containsKey(k) ){
				Set<Item> l = new TreeSet<Item>(new ItemComparator());
				l.add(B);
				Keywords.put(k, l);
			}
			else{
				Keywords.get(k).add(B);
			}
		}
		
//		category by author
		if( !BookByAuthor.containsKey(author)){
			Set<Item> l = new TreeSet<Item>(new ItemComparator());
			l.add(B);
			BookByAuthor.put(author, l);
		}
		else{
			BookByAuthor.get(author).add(B);
		}
		
		//add to Books
		Books.add(B);
		
		return B;
	}
	
	// removes a book from the library (only needed for Premium version)
	public boolean removeBook(String title)
	{
		for( Item I: Books){
			if( I.getTitle().equals( title ) ){
				
				//remove item in bookByAuthor
				BookByAuthor.get( I.getCreator() ).remove( I );		
				
				//remove item in keywords
				for(String k: I.getKeyword() ){
					Keywords.get( k ).remove( I );
				}
				
				//remove item in Books
				Books.remove( I );
				
				return true;
			}
		}
		return false;
	}
	
	// returns all of the books by the specified author
	public Collection<Item> booksByAuthor(String author)
	{
		return BookByAuthor.get(author);
	}
	
	// returns all of the books in the library
	public Collection<Item> books()
	{
		return Books;
	}
	
	// music-related methods
	
	// adds a music album to the library
	public Item addMusicAlbum(String title, String band, int nSongs, String... keywords)
	{
		MusicAlbum MA = new MusicAlbum(title, band, nSongs, keywords);
		
		//category by keywords
		for(String k: keywords){
			if ( Keywords.containsKey(k) == false ){
				Set<Item> S = new TreeSet<Item>(new ItemComparator());
				S.add(MA);
				Keywords.put(k, S);
			}
			else{
				Keywords.get(k).add(MA);
			}
		}
		
		//category by band
		if( MusicByBand.containsKey(band) == false){
			Set<Item> S = new TreeSet<Item>(new ItemComparator());
			S.add(MA);
			MusicByBand.put(band, S);
		}
		else{
			MusicByBand.get(band).add(MA);
		}
		
		//add to Books
		MusicAlbums.add(MA);
		
		return MA;
	}

	// adds the specified band members to a music album
	public void addBandMembers(Item album, String... members)
	{
		album.add(members);
		for(String k: members){
			if( MusicByMusician.containsKey(k) == false){
				Set<Item> S = new TreeSet<Item>(new ItemComparator());
				S.add(album);
				MusicByMusician.put(k, S);
			}
			else{
				MusicByMusician.get(k).add(album);
			}
		}
	}
	
	// removes a music album from the library (only needed for Premium version)
	public boolean removeMusicAlbum(String title)
	{
		for( Item I: MusicAlbums){
			if( I.getTitle().equals( title ) ){
				
				//remove item in MusicByBand
				MusicByBand.get( I.getCreator() ).remove(I);		
				
				//remove item in MusicByMusician
				for(String g: I.getGroup() ){
					MusicByMusician.get( g ).remove( I );
				}
				
				//remove item in keywords
				for(String k: I.getKeyword() ){
					Keywords.get( k ).remove( I );
				}
				
				//remove item in MusicAlbums
				MusicAlbums.remove(I);
				
				return true;
			}
		}
		return false;
	}

	// returns all of the music albums by the specified band
	public Collection<Item> musicByBand(String band)
	{
		return MusicByBand.get(band);
	}
	
	// returns all of the music albums by the specified musician
	public Collection<Item> musicByMusician(String musician)
	{
		return MusicByMusician.get(musician);
	}
	
	// returns all of the music albums in the library
	public Collection<Item> musicAlbums()
	{
		return MusicAlbums;
	}
	
	// movie-related methods
	
	// adds a movie to the library
	public Item addMovie(String title, String director, int nScenes, String... keywords)
	{
		Movie MV = new Movie( title, director, nScenes, keywords );
		
		//category by keywords
		for(String k: keywords){
			if ( Keywords.containsKey( k ) == false ){
				Set<Item> S = new TreeSet<Item>( new ItemComparator() );
				S.add( MV );
				Keywords.put( k, S );
			}
			else{
				Keywords.get( k ).add( MV );
			}
		}
		
		//category by Director
		if( MoviesByDirector.containsKey( director ) == false){
			Set<Item> S = new TreeSet<Item>( new ItemComparator() );
			S.add( MV );
			MoviesByDirector.put( director, S );
		}
		else{
			MoviesByDirector.get( director ).add( MV );
		}
		
		//add to Books
		Movies.add( MV );
		
		return MV;
	}

	// adds the specified actors to a movie
	public void addCast( Item movie, String... members )
	{
		movie.add( members );
		for( String k: members ){
			if( MoviesByActor.containsKey( k ) == false){
				Set<Item> S = new TreeSet<Item>( new ItemComparator() );
				S.add( movie );
				MoviesByActor.put( k, S );
			}
			else{
				MoviesByActor.get( k ).add( movie );
			}
		}
	}

	// removes a movie from the library (only needed for Premium version)
	public boolean removeMovie(String title )
	{
		for( Item I: Movies){
			if( I.getTitle().equals( title ) ){
				
				//remove item in MoviesByDirector
				MoviesByDirector.get( I.getCreator() ).remove( I );		
				
				//remove item in MoviesByActor
				for(String g: I.getGroup() ){
					MoviesByActor.get( g ).remove( I );
				}
				
				//remove item in keywords
				for(String k: I.getKeyword() ){
					Keywords.get( k ).remove( I );
				}
				
				//remove item in MusicAlbums
				Movies.remove( I );
				
				return true;
			}
		}
		return false;
	}
	
	// returns all of the movies by the specified director
	public Collection<Item> moviesByDirector(String director )
	{
		return MoviesByDirector.get( director );
	}
	
	// returns all of the movies by the specified actor
	public Collection<Item> moviesByActor(String actor )
	{
		return MoviesByActor.get( actor );
	}
	
	// returns all of the movies in the library
	public Collection<Item> movies()
	{
		return Movies;
	}	
}
